package com.yl.utils;

import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * PCM 文件转wav文件
 *
 * @param
 * @author admin
 * @date 2023/10/8 9:47
 **/
public class PcmToWavUtil {
    /**
     * PCm文件转Wav文件
     *
     * @param
     * @date 2023/10/8 10:22
     **/
    public static void pcmToWav(String sourceFileUrl, String targetFileUrl) throws Exception {
        File pcmfile = new File(sourceFileUrl);//8000Hz,单声道，16位pcm
        if (!pcmfile.exists()) {
            return;
        }
        File wavfile = new File(targetFileUrl);
        FileInputStream fis = new FileInputStream(pcmfile);
        byte channels = 1;
//        int sampleRate = 8000;
        int sampleRate = 16000;
        int byteRate = 16 * sampleRate * channels / 8;
        int datalen = (int) fis.getChannel().size();
        System.out.println(datalen);

        ByteBuffer bb = ByteBuffer.allocate(44);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        //RIFF标记
        bb.put(new byte[]{'R', 'I', 'F', 'F'});
        //原始数据长度（不包含RIFF和本字段共8个字节）
        bb.putInt(datalen + 44 - 8);
        //WAVE标记
        bb.put(new byte[]{'W', 'A', 'V', 'E'});
//fmt标记
        bb.put(new byte[]{'f', 'm', 't', ' '});
        //“fmt”字段的长度，存储该子块的字节数（不含前面的Subchunk1ID和Subchunk1Size这8个字节）
        bb.putInt(16);
        //存储音频文件的编码格式，PCM其存储值为1
        bb.putShort((short) 1);
        //通道数，单通道(Mono)值为1，双通道(Stereo)值为2
        bb.putShort((short) 1);
        //采样率
        bb.putInt(sampleRate);
        //音频数据传送速率,采样率*通道数*采样深度/8。(每秒存储的bit数，其值=SampleRate * NumChannels * BitsPerSample/8)
        bb.putInt(byteRate);
        //块对齐/帧大小，NumChannels * BitsPerSample/8
        bb.putShort((short) (1 * 16 / 8));
        //pcm数据位数，一般为8,16,32等
        bb.putShort((short) 16);
        //data标记
        bb.put(new byte[]{'d', 'a', 't', 'a'});
        //data数据长度
        bb.putInt(datalen);
        byte[] header = bb.array();
        for (int i = 0; i < header.length; i++) {
            System.out.printf("%02x ", header[i]);
        }
        System.out.println();

        ByteBuffer wavbuff = ByteBuffer.allocate(44 + datalen);
        wavbuff.put(header);
        byte[] temp = new byte[datalen];
        fis.read(temp);
        wavbuff.put(temp);
        byte[] wavbytes = wavbuff.array();
        FileOutputStream fos = null;
        fos = new FileOutputStream(wavfile);
        fos.write(wavbytes);
        fos.flush();
        fos.close();
        fis.close();
        System.out.println("finished.");
    }

    /**
     * MP3转换PCM文件方法
     *
     * @param mp3filepath 原始文件路径
     * @param pcmfilepath 转换文件的保存路径
     * @throws Exception
     */
    public static void convertMP32PCM(String mp3filepath, String pcmfilepath) throws Exception {
        //转换PCM audioInputStream 数据
        AudioInputStream audioInputStream = getPcmAudioInputStream(mp3filepath);
        //写入PCM预给定的文件
        AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, new File(pcmfilepath));
    }

    /**
     * 获取PCM AudioInputStream 数据
     *
     * @param mp3filepath
     * @return AudioInputStream
     */
    private static AudioInputStream getPcmAudioInputStream(String mp3filepath) {
        File mp3File = new File(mp3filepath);
        AudioInputStream audioInputStream = null;
        AudioFormat targetFormat = null;
        try {
            AudioInputStream in = null;
            MpegAudioFileReader mp = new MpegAudioFileReader();
            in = mp.getAudioInputStream(mp3File);
            AudioFormat baseFormat = in.getFormat();
            targetFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16,
                    baseFormat.getChannels(), baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false);
            audioInputStream = AudioSystem.getAudioInputStream(targetFormat, in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return audioInputStream;
    }
}
